package org.trc.simulation;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JFileChooser;

import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.shapefile.shp.ShapefileException;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.operation.TransformException;
import org.trc.io.DBAccess;
import org.trc.io.DBUpdate;
import org.trc.network.Edge;
import org.trc.network.Network;
import org.trc.network.RoutePlanning;
import org.trc.traffic.BasicLinkTraffic;
import org.trc.trajectory.Trajectory;
import org.trc.trajectory.TrajectoryPoint;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.linearref.LengthIndexedLine;


public class SimuAnalysis {
	
	/**
	 * 通过车辆轨迹来获取LinkTraffic (端点时差法)
	 * 
	 * 
	 * @param traj
	 * @param road
	 * @return
	 * @throws IOException
	 */
	public static  ArrayList <BasicLinkTraffic> extractBasicLinkTraffic1(STrajectory traj, Network road) throws IOException{		
		//for test
		PrintWriter log = new PrintWriter(new FileWriter("D:\\error.txt", true));
		
		ArrayList <BasicLinkTraffic> linkTrafficList = new ArrayList <BasicLinkTraffic> ();
		
		//采样对
		STrajectoryPoint prePoint = null;
		STrajectoryPoint nextPoint = null;
		
		//端点
		int preNodeEnterTime = 0;
		int curNodeEnterTime = 0;
		
		//内插
		int preTime = 0;
		int nextTime = 0;
		
		//路段行程时间_速度
		int travelTime = 0;
		float travelSpeed = 0f;
		
		//对轨迹进行遍历
		for(int i = 0; i < traj.points.size()- 1; i++) {
			prePoint = traj.points.get(i);
			nextPoint = traj.points.get(i+1);
			
			//位于同一路段
			if(prePoint.linkID.equals(nextPoint.linkID))
				continue;
			
			//如果位于邻接路段
			if(road.getCommonPoint(prePoint.linkID, nextPoint.linkID) != null) {
				
				Edge preEdge = road.getEdgeById(prePoint.linkID);
				
				//Edge curEdge = road.getEdgeById(nextPoint.linkID);
				
				double preDistance =  preEdge.length - prePoint.offset;
				double nextDistance = nextPoint.offset;
				
				preTime = prePoint.upSecond;				
				nextTime = nextPoint.upSecond;
				
				curNodeEnterTime = (int) ((preTime*nextDistance + nextTime*preDistance)/(preDistance + nextDistance)); //内插
				
				if(preNodeEnterTime != 0) {//进出点判断，如果等于0，表明为进入点
					
					travelTime = Math.abs(curNodeEnterTime - preNodeEnterTime);
					
					if(travelTime < 1) {
						travelTime = 1;
						System.out.println("Note: The travel time is smaller than 1s");
					}
						
					travelSpeed = (float) (preEdge.length/travelTime);
		
					//生成BasicLinkTraffic
					BasicLinkTraffic linkTraffic = new BasicLinkTraffic(traj.carID, prePoint.linkID, preNodeEnterTime, travelTime, travelSpeed);
					
					if(travelSpeed > 20) { //异常速度
						
					 String record = "car: " + linkTraffic.carID + ", "  
					                + "linkID: " + linkTraffic.linkID + ", " 
					                + "enterTime: " + linkTraffic.enterTime + ", " 
							        + "travleTime : " + travelTime + ", " 
							        + "speed: "+ travelSpeed; 
				     log.println(record);

					 System.out.println(record); 
					}
					
					linkTrafficList.add(linkTraffic);
					
					preNodeEnterTime = curNodeEnterTime;
					
				}else {
					
					preNodeEnterTime = curNodeEnterTime;	
					
				}	
			}else {//跨越多路段
				
				RoutePlanning rp = new RoutePlanning(road);
				
				if(rp.computeFastestWay(prePoint.linkID, prePoint.offset, 2, nextPoint.linkID, nextPoint.offset, 2) != true) { //无法恢复路径
					preNodeEnterTime = 0;
					continue;
				}
				
				ArrayList<Edge> route = rp.getRoute(); //路径恢复
				
				float routeLength = rp.getRouteLength();
				
				Edge firstEdge = (Edge) route.get(0); //获取第一条边
				
				if(preNodeEnterTime != 0) {//进出点判断
					
					curNodeEnterTime =  prePoint.upSecond +  (int)(firstEdge.length/routeLength * (nextPoint.upSecond - prePoint.upSecond));
									
					travelTime = Math.abs(curNodeEnterTime - preNodeEnterTime);
					
					if(travelTime < 1) {
						travelTime = 1;
						System.out.println("Note: The travel time is smaller than 1s");
					}
					
					travelSpeed = (float) ((prePoint.offset + firstEdge.length)/travelTime);
					
					//生成BasicLinkTraffic
					BasicLinkTraffic linkTraffic = new BasicLinkTraffic(traj.carID, prePoint.linkID, preNodeEnterTime, travelTime, travelSpeed);
					
				    if(travelSpeed > 20) {
						String record = "car: " + linkTraffic.carID + ", "  
						                + "linkID: " + linkTraffic.linkID + ", " 
						                + "enterTime: " + linkTraffic.enterTime + ", " 
								        + "travleTime : " + travelTime + ", " 
								        + "speed: "+ travelSpeed; 
						log.println(record);
					     
					    System.out.println(record); 
				   }
					
				   linkTrafficList.add(linkTraffic);
					
				   preNodeEnterTime = curNodeEnterTime;
					
				}else {//起始边
					preNodeEnterTime = prePoint.upSecond +  (int) (firstEdge.length/routeLength *(nextPoint.upSecond - prePoint.upSecond));
		
				}
				
				for(int j = 1; j < route.size()-1; j++){
					Edge edge = (Edge)route.get(j);
					
				    travelTime = (int) (edge.length/routeLength *(nextPoint.upSecond - prePoint.upSecond));
				    
					if(travelTime < 1) {
						travelTime = 1;
						System.out.println("Note: The travel time is smaller than 1s");
					}
						
				    travelSpeed = (float) (edge.length/travelTime);
						
				    //生成BasicLinkTraffic
				    BasicLinkTraffic linkTraffic = new BasicLinkTraffic(traj.carID, edge.id, preNodeEnterTime, travelTime, travelSpeed);
				    
					if(travelSpeed > 20) {
						String record = "car: " + linkTraffic.carID + ", "  
						                + "linkID: " + linkTraffic.linkID + ", " 
						                + "enterTime: " + linkTraffic.enterTime + ", " 
								        + "travleTime : " + travelTime + ", " 
								        + "speed: "+ travelSpeed; 
						log.println(record);
						
						System.out.println(record); 
					}
						
				    linkTrafficList.add(linkTraffic);
					
					preNodeEnterTime = preNodeEnterTime + travelTime;
				}		
			}
		}
		
		log.close();
		
		return linkTrafficList;
	}

	/**
	 * 通过车辆轨迹来获取LinkTraffic (路径速度加权法)
	 * 
	 * 
	 * @param traj
	 * @param road
	 * @return
	 */
	public static ArrayList <BasicLinkTraffic> extractBasicLinkTraffic2(STrajectory traj, Network road, int startTime, int endTime, int interval) {
		ArrayList <BasicLinkTraffic> linkTrafficList = new ArrayList <BasicLinkTraffic> ();
		
		int maxPeroidNum = (endTime -startTime)/interval + 1; // 最大统计间隔数 （startTime <= uptime < endTime)
		int startPeroidNum = 0; // 起始间隔索引
		
		for(STrajectoryPoint point: traj.points){			
			if(point.upSecond - startTime > 0){
				startPeroidNum = (point.upSecond - startTime)/interval + 1;	
				break;
			}		
		}
							
		int trajIndex = 0;
		
		for(int i = startPeroidNum; i <= maxPeroidNum; i++) { // i 代表 时间间隔索引
			int fromIndex = trajIndex;
			int toIndex = trajIndex;
			
			for(int j = trajIndex; j < traj.points.size(); j++){
				STrajectoryPoint trajPoint = traj.points.get(j);
				if((trajPoint.upSecond - startTime)/interval + 1 != i){
					toIndex = j;				
					break;
				}
				if(j == traj.points.size()-1)
					toIndex = j;
			}
			
			if(fromIndex == toIndex) 
				break;
			
			ArrayList <BasicLinkTraffic> linkTrafficSubList = extractBasicLinkSpeed(traj.carID, startTime+interval*(i-1), traj.points.subList(fromIndex, toIndex), road); //获取同一个周期里的点
		
			if(linkTrafficSubList!= null)
				linkTrafficList.addAll(linkTrafficSubList);
			
			trajIndex = toIndex;
		}
		
		return linkTrafficList;
	}

	private static ArrayList <BasicLinkTraffic> extractBasicLinkSpeed(String carid, int enterTime, List<STrajectoryPoint> points, Network road){
		
		ArrayList <BasicLinkTraffic> linkTrafficList = new ArrayList <BasicLinkTraffic> ();
		
		ArrayList <Edge> route = new ArrayList <Edge> (); //恢复轨迹
		float length = 0f; //路径总长
		
		System.out.println(enterTime);
		
		int time = points.get(points.size() - 1).upSecond - points.get(0).upSecond; //总的行程时间
		float avg = 0f;
		
		//起始边	
		Edge startEdge = road.getEdgeById((points.get(0).linkID));
		route.add(startEdge);
		length = (float) ((startEdge.length  - points.get(0).offset));
		
		//采样对
		STrajectoryPoint prePoint = null;
		STrajectoryPoint nextPoint = null;
		
		for(int i = 0; i < points.size()-1; i++){
			prePoint = points.get(i);
			nextPoint = points.get(i+1);
					
			Edge nextEdge = null;
			
			if(prePoint.linkID.equals(nextPoint.linkID)){ //位于同一路段			
				continue;
				
			}else if(road.getCommonPoint(prePoint.linkID, nextPoint.linkID) != null) {//相邻路段
				
				nextEdge = road.getEdgeById(nextPoint.linkID);
				
				route.add(nextEdge);
				
				length += nextEdge.length;
				
			}else { // 跨路段
				RoutePlanning rp = new RoutePlanning(road);
				
				if(rp.computeFastestWay(prePoint.linkID, prePoint.offset, 2, nextPoint.linkID, nextPoint.offset, 2) != true) { //无法恢复路径
					System.out.println("Trajectory Recovery Failed");
					return null;
				}
				
				ArrayList<Edge> subRoute = rp.getRoute(); //获取路径
				
				route.addAll(subRoute.subList(1, subRoute.size()- 1)); //加入路径上的非虚拟边
				
				length = (float) (length + rp.getRouteLength() - subRoute.get(0).length - subRoute.get(subRoute.size()- 1).length);
				
				nextEdge = road.getEdgeById(nextPoint.linkID); //加入非虚拟边
				
				route.add(nextEdge);
								
				length += nextEdge.length;
			}
		}
		
		//结束边
		Edge endEdge = road.getEdgeById(points.get(points.size()-1).linkID);		
		length = (float) (length - (endEdge.length  - points.get(points.size()-1).offset));
		
		avg = length/time;
		
		if(startEdge.id.equals(endEdge.id)){//起止点位于同一路段
			BasicLinkTraffic onlyLinkTraffic = new BasicLinkTraffic(carid, startEdge.id, enterTime, 0,  avg, (float) (length/startEdge.length));
			
			linkTrafficList.add(onlyLinkTraffic);
			
			System.out.println("OnlyEdge:" + onlyLinkTraffic.linkID + " ,EnterTime:" + onlyLinkTraffic.enterTime + " ,Speed:" + onlyLinkTraffic.travelSpeed + ", Weight:" + onlyLinkTraffic.weight);
			
			return linkTrafficList;
		}
		
		//处理起止边
		BasicLinkTraffic startLinkTraffic = new BasicLinkTraffic(carid, startEdge.id, enterTime, 0,  avg, (float) ((startEdge.length - points.get(0).offset)/startEdge.length));
		BasicLinkTraffic endLinkTraffic = new BasicLinkTraffic(carid, endEdge.id, enterTime, 0,  avg, (float) ((points.get(points.size()-1).offset)/endEdge.length));
		
		linkTrafficList.add(startLinkTraffic);
		
		System.out.println("StartEdge:" + startLinkTraffic.linkID + " ,EnterTime:" + startLinkTraffic.enterTime + " ,Speed:" + startLinkTraffic.travelSpeed + ", Weight:" + startLinkTraffic.weight);
		
		if(route.size() == 2) {			
			linkTrafficList.add(endLinkTraffic);
			
			System.out.println("EndEdge:" + endLinkTraffic.linkID + " ,EnterTime:" + endLinkTraffic.enterTime + " ,Speed:" + endLinkTraffic.travelSpeed + ", Weight:" + endLinkTraffic.weight);
			
			return linkTrafficList;		
		}
		
		for(Edge routeEdge: route.subList(1, route.size()-1)){
			
			BasicLinkTraffic linkTraffic = new BasicLinkTraffic(carid, routeEdge.id, enterTime, 0,  avg, 1f);
			
			linkTrafficList.add(linkTraffic);
			
			System.out.println("Edge:" + linkTraffic.linkID + " ,EnterTime:" + linkTraffic.enterTime + " ,Speed:" + linkTraffic.travelSpeed + ", Weight:" + linkTraffic.weight);
		}
		
		linkTrafficList.add(endLinkTraffic);
		
		System.out.println("EndEdge:" + endLinkTraffic.linkID + " ,EnterTime:" + endLinkTraffic.enterTime + " ,Speed:" + endLinkTraffic.travelSpeed + ", Weight:" + endLinkTraffic.weight);
			
		return linkTrafficList;
	}
	
	/**
	 * 获取车辆轨迹的shapefile
	 * 
	 * @param cid
	 * @param startTime
	 * @param endTime
	 * @param road
	 * @throws SchemaException
	 * @throws IOException
	 */
	public static void extractVehicleTrajectory(String cid, int startTime, int endTime, Network road) throws SchemaException, IOException{
		STrajectory traj = DBAccess.getVissimTrajectoryByCID(cid, startTime, endTime);
		
		// write new shape file
		JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
	    chooser.setDialogTitle("Save a new shapefile for a trajectory");
	    chooser.setSaveFile(new File("D:\\trjectory.shp"));	    
        if (chooser.showSaveDialog(null) != JFileChooser.APPROVE_OPTION) {
            return;
        }
        File outputShapefile = chooser.getSelectedFile();
		
		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put("url", outputShapefile.toURI().toURL());
		params.put("create spatial index", Boolean.TRUE);
		ShapefileDataStore writeDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
		      
		// specify the feature type
		final SimpleFeatureType TYPE = DataUtilities.createType(
				"SamplePoint",
				"GEOM:Point:srid=4326," 
				+ "CID:String," 
				+ "NUM:int,"
				+ "LINKID:double,"
				+ "OFFSET:double,"
				+ "UPTIME:String,"
				+ "SPEED:String");

		writeDataStore.createSchema(TYPE);
		writeDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		//prepare new feature collection	
		SimpleFeatureCollection collection = FeatureCollections.newCollection();
		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

		Point geom = null;
		String carid = null;
		int num = 0;
		String linkID = null;
		Double offset = null;
        Integer uptime = null;
		Float speed = null;
				
		SimpleFeature feature = null;
		
		GeometryFactory factory = new GeometryFactory();
		
		for(STrajectoryPoint point : traj.points) {
			System.out.println(num);
			
			LengthIndexedLine indexedLine = new LengthIndexedLine(road.getEdgeById(point.linkID).getLineString());
			
			double index = indexedLine.project(new Coordinate(point.x, point.y));
			
			geom = factory.createPoint(indexedLine.extractPoint(index));
			
			carid = cid;
			num++;
			linkID = point.linkID;
			offset = Double.valueOf(point.offset);
			uptime = Integer.valueOf(point.upSecond);
			speed = Float.valueOf(point.speed);
			
			// create feature
			featureBuilder.add(geom);
			featureBuilder.add(carid);
			featureBuilder.add(num);
			featureBuilder.add(linkID);
			featureBuilder.add(offset);
			featureBuilder.add(uptime);
			featureBuilder.add(speed);
			feature = featureBuilder.buildFeature(null);
			collection.add(feature);
		}

		//create transaction
		Transaction transaction = new DefaultTransaction("create");
		String typeName = writeDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = writeDataStore.getFeatureSource(typeName);

		if (featureSource instanceof SimpleFeatureStore) {
			SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
			featureStore.setTransaction(transaction);
			try {
				featureStore.addFeatures(collection);
				transaction.commit();
			} catch (Exception problem) {
				problem.printStackTrace();
				transaction.rollback();
			} finally {
				transaction.close();
			}
			System.exit(0); // success!
		} else {
			System.out.println(typeName + " does not support read/write access");
			System.exit(1);
		}
		
	}
		
	public static void main(String[] args) throws ShapefileException, IOException, SchemaException {
		     
		Network road = new Network("E:\\Data\\Road\\Wuhan_2010.9\\simu_road.shp", true);
		
        BufferedReader reader = new BufferedReader(new FileReader("E:\\Data\\Simulation\\vehicle.csv"));
    	
	    String line = reader.readLine();
	    
	    String[] dataEntry = null;
	    
	    String carid = null;
	    int i = 0;
	    
	    while (line != null) {//行遍历
	    	i++;
	    	dataEntry = line.split(",");
	    	carid = (String)dataEntry[0].trim();
	    	System.out.println(i + ":" + carid + ":-------");
			STrajectory traj = DBAccess.getVissimTrajectoryByCID(carid, 2000, 3200);		
			ArrayList<BasicLinkTraffic> basicLinkTraffic = SimuAnalysis.extractBasicLinkTraffic2(traj, road, 2000, 3200, 300);
			DBUpdate.populateVissimBasicTrafficTable2(basicLinkTraffic);
			line = reader.readLine();
		}
	
       // SimuAnalysis.extractVehicleTrajectory("8914", 2000, 3200, road);
        
//		STrajectory traj = DBAccess.getVissimTrajectoryByCID("8914", 2000, 3200);
//	
//		SimuAnalysis.extractBasicLinkTraffic2(traj, road, 2000, 3200, 300);
		
		//		STrajectory traj = DBAccess.getVissimTrajectoryByCID("7629", 2000, 3200);
		
//		SimuAnalysis.extractBasicLinkTraffic(traj, road);

	}

}
